<template>
    <view class="cmd-circle">
        <canvas :canvas-id="cid" :style="calCircleStyle"></canvas>
    </view>
</template>

<script>
/**
 * 进度圈组件
 * @description 用圈显示一个操作完成的百分比时，为用户显示该操作的当前进度和状态。
 * @tutorial https://ext.dcloud.net.cn/plugin?id=259
 * @property {String} cid 画布编号 - 默认defaultCanvas
 * @property {String} type 进度圈类型 - 圆圈形：circle、仪表盘：dashboard，默认圆圈形：circle
 * @property {Number} percent 进度圈百分比值 - 显示范围0-100 ，可能数比较大就需要自己转成百分比的值
 * @property {Boolean} show-info 进度圈进度状态信息 - 显示进度数值或状态图标，默认true
 * @property {String} font-color 进度圈文字信息颜色
 * @property {String} font-size 进度圈文字信息大小 - 默认：14
 * @property {String} status 进度圈状态 - 正常：normal、完成：success、失败：exception，默认正常：normal
 * @property {Number} stroke-width 进度圈线条宽度 - 建议在条线的宽度范围：1-50，与进度条显示宽度有关，默认：6
 * @property {String} stroke-color 进度圈的颜色 - 设置后status状态无效
 * @property {String} stroke-background 进度圈的底圈颜色 - 默认：#eeeeee
 * @property {String} stroke-shape 进度圈两端的形状 - 圆：round、方直角：square，默认圆：round
 * @property {Number} width 进度圈布宽度 - 默认80
 * @property {String} gap-degree 进度圈形缺口角度 - 可取值 0 ~ 360，仅支持类型：circle
 * @property {String} gap-position 进度圈形缺口位置 - 可取值'top', 'bottom', 'left', 'right'，仅支持类型：circle
 * @example <cmd-circle id="circle1" type="circle" :percent="75"></cmd-circle>
 */
export default {
    name: 'CmdCircle',

    props: {
        // 画布编号 默认defaultCanvas
        cid: {
            type: String,
            default: 'defaultCanvas'
        },
        // 圈类型默认：circle，可选 circle dashboard
        type: {
            type: String,
            validator: (val) => {
                return ['circle', 'dashboard'].includes(val)
            },
            default: 'circle'
        },
        // 圈进度百分比值
        percent: {
            type: Number,
            validator: (val) => {
                return val >= 0 && val <= 100
            },
            default: 0
        },
        // 圈是否显示进度数值或状态图标
        showInfo: {
            type: Boolean,
            default: true
        },
        // 圈文字信息颜色
        fontColor: {
            type: String,
            default: '#595959'
        },
        // 圈文字信息大小 默认14
        fontSize: {
            type: Number,
            default: 14
        },
        // 圈进度状态，可选：normal success exception
        status: {
            type: String,
            validator: (val) => {
                return ['normal', 'success', 'exception'].includes(val)
            },
            default: 'normal'
        },
        // 圈线条宽度1-50，与width有关
        strokeWidth: {
            type: Number,
            default: 6
        },
        // 圈的颜色，设置后status状态无效
        strokeColor: {
            type: String,
            default: ''
        },
        // 圈的底圈颜色 默认：#eeeeee
        strokeBackground: {
            type: String,
            default: '#eeeeee'
        },
        // 圈两端的形状 可选：'round', 'square'
        strokeShape: {
            type: String,
            validator: (val) => {
                return ['round', 'square'].includes(val)
            },
            default: 'round'
        },
        // 圈画布宽度
        width: {
            type: Number,
            default: 80
        },
        // 圈缺口角度，可取值 0 ~ 360，仅支持类型：circle
        gapDegree: {
            type: Number,
            validator: (val) => {
                return val >= 0 && val <= 360
            },
            default: 360
        },
        // 圈缺口开始位置,可取值'top', 'bottom', 'left', 'right'，仅支持类型：circle
        gapPosition: {
            type: String,
            validator: (val) => {
                return ['top', 'bottom', 'left', 'right'].includes(val)
            },
            default: 'top'
        }
    },

    data() {
        return {
            // 画布实例
            ctx: {},
            // 圈半径
            width2px: ''
        }
    },

    computed: {
        // 计算设置圈样式
        calCircleStyle() {
            return `width: ${this.width * 2}rpx;
				height: ${this.width * 2}rpx;`
        },
        // 计算圈状态
        calStatus() {
            let status = {}
            switch (this.status) {
                case 'normal':
                    status = {
                        color: '#1890ff',
                        value: 1
                    }
                    break
                case 'success':
                    status = {
                        color: '#52c41a',
                        value: 2
                    }
                    break
                case 'exception':
                    status = {
                        color: '#f5222d',
                        value: 3
                    }
                    break
            }
            return status
        },
        // 计算圈缺口角度
        calGapDegree() {
            return this.gapDegree <= 0 ? 360 : this.gapDegree
        },
        // 计算圈缺口位置
        calGapPosition() {
            let gapPosition = 0
            switch (this.gapPosition) {
                case 'bottom':
                    gapPosition = 90
                    break
                case 'left':
                    gapPosition = 180
                    break
                case 'top':
                    gapPosition = 270
                    break
                case 'right':
                    gapPosition = 360
                    break
            }
            return gapPosition
        }
    },

    watch: {
        // 监听百分比值改变
        percent(val) {
            this.drawStroke(val)
        }
    },

    mounted() {
        // 创建画布实例
        this.ctx = uni.createCanvasContext(this.cid, this)
        // upx转px 圈半径大小
        this.width2px = uni.upx2px(this.width)
        // 绘制初始
        this.$nextTick(() => {
        	setTimeout(() => {
        		this.drawStroke(this.percent)
        	}, 10) // 延迟生成 解决切换时画布不出现问题
        })
    },

    methods: {
        // 绘制圈
        drawStroke(percent) {
            percent = percent >= 100 ? 100 : percent < 0 ? 0 : percent
            // 圈条进度色
            let color = this.strokeColor || this.calStatus.color
            // 是否圈中心显示信息
            if (this.showInfo) {
                switch (this.calStatus.value) {
                    case 1:
                        if (percent >= 100) {
                            // 设置打勾
                            this.drawSuccess()
                            percent = 100
                            color = '#52c41a'
                        } else {
                            // 设置字体
                            this.drawText(percent)
                        }
                        break
                    case 2:
                        // 设置打勾
                        this.drawSuccess()
                        percent = 100
                        color = '#52c41a'
                        break
                    case 3:
                        // 设置打叉
                        this.drawException()
                        percent = 0
                        color = '#f5222d'
                        break
                    default:
                        break
                }
            }
            // 缺口
            let gapPosition = this.calGapPosition
            let gapDegree = this.calGapDegree
            // 仪表固定
            if (this.type === 'dashboard') {
                gapPosition = 135
                gapDegree = 270
            }
            // 圈型条宽
            this.ctx.setLineCap(this.strokeShape)
            this.ctx.setLineWidth(this.strokeWidth)
            // 位置原点
            this.ctx.translate(this.width2px, this.width2px)
            // 缺口方向
            this.ctx.rotate((gapPosition * Math.PI) / 180)
            // 圈底
            this.ctx.beginPath()
            this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, (gapDegree * Math.PI) / 180)
            this.ctx.setStrokeStyle(this.strokeBackground)
            this.ctx.stroke()
            // 圈进度
            this.ctx.beginPath()
            this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, (percent * gapDegree * Math.PI) / 18000)
            this.ctx.setStrokeStyle(color)
            this.ctx.stroke()
            // 绘制
            this.ctx.draw(false, setTimeout(() => { // 回调函数，通知cover-view可以渲染了
            	this.$emit('done')
            }, 150))
        },
        // 绘制文字格式
        drawText(percent) {
            this.ctx.beginPath()
            this.ctx.setFontSize(this.fontSize)
            this.ctx.setFillStyle(this.fontColor)
            this.ctx.setTextAlign('center')
            this.ctx.fillText(`${percent}%`, this.width2px, this.width2px + this.fontSize / 2)
            this.ctx.stroke()
        },
        // 绘制成功打勾
        drawSuccess() {
            const x = this.width2px - this.fontSize / 2
            const y = this.width2px + this.fontSize / 2
            this.ctx.beginPath()
            this.ctx.setLineCap('round')
            this.ctx.setLineWidth(this.fontSize / 4)
            this.ctx.moveTo(this.width2px, y)
            this.ctx.lineTo(y, x)
            this.ctx.moveTo(this.width2px, y)
            this.ctx.lineTo(x, this.width2px)
            this.ctx.setStrokeStyle('#52c41a')
            this.ctx.stroke()
        },
        // 绘制异常打叉
        drawException() {
            const x = this.width2px - this.fontSize / 2
            const y = this.width2px + this.fontSize / 2
            this.ctx.beginPath()
            this.ctx.setLineCap('round')
            this.ctx.setLineWidth(this.fontSize / 4)
            this.ctx.moveTo(x, x)
            this.ctx.lineTo(y, y)
            this.ctx.moveTo(y, x)
            this.ctx.lineTo(x, y)
            this.ctx.setStrokeStyle('#f5222d')
            this.ctx.stroke()
        }
    }
}
</script>

<style>
.cmd-circle {
    display: inline-block;
    box-sizing: border-box;
    list-style: none;
    margin: 0;
    padding: 0;
}
</style>
